package cn.ivicar.mapper.replicable;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ReplicableSqlSession implements SqlSession {
    private static final transient Logger LOGGER = LoggerFactory.getLogger(ReplicableSqlSession.class);
    private SqlSession session;
    private boolean needRestore;

    public ReplicableSqlSession(SqlSession session, boolean readOnly)
    {
        this.session = session;
        this.needRestore = false;
        Connection conn = session.getConnection();
        this.needRestore = setReadOnly(conn, readOnly);
    }

    public Object selectOne(String statement) {
        return this.session.selectOne(statement);
    }

    public Object selectOne(String statement, Object parameter) {
        return this.session.selectOne(statement, parameter);
    }

    public List selectList(String statement) {
        return this.session.selectList(statement);
    }

    public List selectList(String statement, Object parameter) {
        return this.session.selectList(statement, parameter);
    }

    public List selectList(String statement, Object parameter, RowBounds rowBounds) {
        return this.session.selectList(statement, parameter, rowBounds);
    }

    public Map selectMap(String statement, String mapKey) {
        return this.session.selectMap(statement, mapKey);
    }

    public Map selectMap(String statement, Object parameter, String mapKey) {
        return this.session.selectMap(statement, parameter, mapKey);
    }

    public Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
        return this.session.selectMap(statement, parameter, mapKey, rowBounds);
    }

    public void select(String statement, Object parameter, ResultHandler handler) {
        this.session.select(statement, parameter, handler);
    }

    public void select(String statement, ResultHandler handler) {
        this.session.select(statement, handler);
    }

    public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
        this.session.select(statement, parameter, rowBounds, handler);
    }

    public int insert(String statement) {
        return this.session.insert(statement);
    }

    public int insert(String statement, Object parameter) {
        return this.session.insert(statement, parameter);
    }

    public int update(String statement) {
        return this.session.update(statement);
    }

    public int update(String statement, Object parameter) {
        return this.session.update(statement, parameter);
    }

    public int delete(String statement) {
        return this.session.delete(statement);
    }

    public int delete(String statement, Object parameter) {
        return this.session.delete(statement, parameter);
    }

    public void commit() {
        this.session.commit();
    }

    public void commit(boolean force) {
        this.session.commit(force);
    }

    public void rollback() {
        this.session.rollback();
    }

    public void rollback(boolean force) {
        this.session.rollback(force);
    }

    public void close() {
        if (this.needRestore == true) {
            restoreReadOnly(this.session.getConnection(), this.needRestore);
        }

        this.session.close();
    }

    public void clearCache() {
        this.session.clearCache();
    }

    public Configuration getConfiguration() {
        return this.session.getConfiguration();
    }

    public <T> T getMapper(Class<T> type) {
        return this.session.getMapper(type);
    }

    public Connection getConnection() {
        return this.session.getConnection();
    }

    private void restoreReadOnly(Connection conn, boolean needRestored) {
        if (needRestored == true) {
            boolean current = false;
            try {
                current = conn.isReadOnly();
            } catch (SQLException e) {
                LOGGER.trace(e.getMessage());
                return;
            }

            try
            {
                current = !current;
                conn.setReadOnly(current);
            } catch (SQLException e) {
                LOGGER.trace(e.getMessage());
            }
        }
    }

    protected boolean setReadOnly(Connection conn, boolean readOnly) {
        boolean needToSet = false;
        try
        {
            needToSet = conn.isReadOnly() != readOnly;
        } catch (SQLException e) {
            LOGGER.trace(e.getMessage());
            return false;
        }

        if (needToSet == true) {
            try {
                conn.setReadOnly(readOnly);
            } catch (SQLException e) {
                LOGGER.trace(e.getMessage());
                return false;
            }

            return true;
        }

        return needToSet;
    }

    public List<BatchResult> flushStatements() {
        // TODO Auto-generated method stub
        return this.session.flushStatements();
    }
}
